package org.cloud.dimension.aop;

import static org.cloud.constant.CoreConstant.USER_LOGIN_SUCCESS_CACHE_KEY;
import static org.cloud.constant.CoreConstant.getLoginIpCacheKey;
import static org.cloud.constant.LoginTypeConstant.LoginTypeEnum.LOGIN_BY_ADMIN_USER;
import static org.cloud.constant.UnauthorizedConstant.LOGIN_TOKEN_ERROR;
import static org.cloud.constant.UnauthorizedConstant.LOGIN_UNAUTHORIZED;

import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.cloud.constant.CoreConstant;
import org.cloud.constant.CoreConstant.AuthMethod;
import org.cloud.constant.UnauthorizedConstant;
import org.cloud.context.RequestContextManager;
import org.cloud.core.redis.RedisUtil;
import org.cloud.dimension.annotation.SystemResource;
import org.cloud.entity.LoginUserDetails;
import org.cloud.exception.BusinessException;
import org.cloud.utils.HttpServletUtil;
import org.cloud.utils.IPUtil;
import org.cloud.utils.MD5Encoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;

@Aspect
@Slf4j
public class SystemResourceAspect {

    @Value("${spring.application.noGroupName:}")
    private String microName;
    private final List<String> excludedAuthPages;
    @Autowired
    private Environment env;

    public SystemResourceAspect(List<String> excludedAuthPages) {
        this.excludedAuthPages = excludedAuthPages;
    }


    @Pointcut("@annotation(org.cloud.dimension.annotation.SystemResource)")
    public void systemResource() {
    }

    @Around("systemResource()")
    public Object getSystemResource(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = HttpServletUtil.single().getHttpServlet();
        boolean isExcludeUri = HttpServletUtil.single().isExcludeUri(request, excludedAuthPages);
        // 如果是权限排除的接口，那么不走权限验证，直接放过。
        if (isExcludeUri || HttpServletUtil.single().isExcludeUri(request, "/inner/**/*")) {
            return joinPoint.proceed();
        }
        //用的最多通知的签名
        Signature signature = joinPoint.getSignature();
        MethodSignature msg = (MethodSignature) signature;
        Object target = joinPoint.getTarget();
        final SystemResource classResourceAnnotation = target.getClass().getAnnotation(SystemResource.class);
        //获取注解标注的方法
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        //通过方法获取注解
        final SystemResource systemResource = method.getAnnotation(SystemResource.class);
        // 如果是不校验，那么全部用户均可以通过
        if (systemResource.authMethod().equals(AuthMethod.NOAUTH)) {
            return joinPoint.proceed();
        }
        LoginUserDetails loginUserDetails = RequestContextManager.single().getRequestContext().getUser();
        if (loginUserDetails == null) {
            throw new BusinessException(LOGIN_UNAUTHORIZED.value(), LOGIN_UNAUTHORIZED.description(), HttpStatus.UNAUTHORIZED.value());
        }
        String token = request.getHeader("authorization");
        final String successKey = MD5Encoder.encode("basic " + StrUtil.split(token, " ").get(1));
        if (loginUserDetails.getUserType().equals(LOGIN_BY_ADMIN_USER.userType) && !request.getRequestURI().startsWith("/inner")) {
//            LoginTypeEnum loginIp = RedisUtil.single().hashGet(LOGIN_TOKEN_IP_MAP_KEY, successKey);
            String loginIp = (String) RedisUtil.getRedisTemplate()
                .opsForValue()
                .get(getLoginIpCacheKey(loginUserDetails.getId(), successKey));
            String requestIp = IPUtil.single().getIpAddress(request);
            if (!requestIp.equalsIgnoreCase(loginIp)) {
                throw new BusinessException(LOGIN_TOKEN_ERROR.value(), LOGIN_TOKEN_ERROR.description(), HttpStatus.UNAUTHORIZED.value());
            }
        }
        // 如果只做用户登录的校验
        if (!systemResource.authMethod().equals(AuthMethod.BYUSERPERMISSION)) {
            return joinPoint.proceed();
        }
        // 这个位置不加前缀，也就是说commonservice不能配置cache的前缀
        Set<String> userFunctions = RedisUtil.single()
            .hashGet(USER_LOGIN_SUCCESS_CACHE_KEY + loginUserDetails.getId(), CoreConstant.UserCacheKey.FUNCTION.value());
        final String functionSetStr = microName + CoreConstant._FUNCTION_SPLIT_STR + env.resolvePlaceholders(classResourceAnnotation.path())
            + CoreConstant._FUNCTION_SPLIT_STR + env.resolvePlaceholders(systemResource.value());
        if (userFunctions == null || userFunctions.isEmpty() || !userFunctions.contains(functionSetStr)) {
            log.error("{},正在非法的请求：{}", loginUserDetails.getUsername(), functionSetStr);
            throw new BusinessException(UnauthorizedConstant.API_UNAUTHORIZED.value(), UnauthorizedConstant.API_UNAUTHORIZED.description(),
                HttpStatus.UNAUTHORIZED.value());
        }
        return joinPoint.proceed();
    }
}

